// !定义类
class Point {
  x: number;
  y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
  getPosition() {
    return (`${this.x},${this.y}`)
  }
}
const point = new Point(1, 1);
// console.log(point); //Point {x: 1, y: 1}
// console.log(point.getPosition()); //1,1

// #继承复用
class Parent {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
class Child extends Parent {
  constructor(name: string) {
    super(name);
  }
}

// !修饰符
// 在 ES6 标准类的定义中，默认情况下，定义在实例的属性和方法会在创建实例后添加到实例上；而如果是定义在类里没有定义在 this 上的方法，实例可以继承这个方法；而如果使用 static 修饰符定义的属性和方法，是静态属性和静态方法，实例是没法访问和继承到的；我们还通过一些手段，实现了私有方法，但是私有属性的实现还不好实现。

//TS修饰符
// @(1)public 公共的,用来指定在创建实例后可以通过实例访问的,也就是类定义的外部可以访问的属性和方法
class Point2 {
  x: number;
  y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
  public getPosition() {
    return (`${this.x},${this.y}`)
  }
}
// @(2)private 私有的,他修饰的属性在类的定义外面是没法访问的
class ageParent {
  private age: number;
  constructor(age: number) {
    this.age = age;
  }
}
const p = new ageParent(18);
console.log(p); //ageParent {age: 18}
// console.log(p.age); //属性“age”为私有属性，只能在类“ageParent”中访问。
// console.log(ageParent.age); //类型“typeof ageParent”上不存在属性“age”。
class ageChild extends ageParent {
  constructor(age: number) {
    super(age);
    // console.log(super.age); //属性“age”为私有属性，只能在类“ageParent”中访问。
  }
}

// @protected protected修饰符是受保护修饰符，和private有些相似，但有一点不同，protected修饰的成员在继承该类的子类中可以访问
class protectedParent {
  protected age: number;
  constructor(age: number) {
    this.age = age;
  }
  protected getAge() {
    return this.age
  }
}
const p2 = new protectedParent(18);
console.log(p2); //ageParent {age: 18}
// console.log(p2.age); //属性“age”受保护，只能在类“protectedParent”及其子类中访问。
// console.log(protectedParent.age); //类型“typeof protectedParent”上不存在属性“age”。
class protectedChild extends protectedParent {
  constructor(age: number) {
    super(age);
    console.log(super.age); //protectedParent {age: 18}
    console.log(super.getAge());

  }
}
new protectedChild(18) // undefined 18 运行这行 上面两个console.log分别输出undefined和18
// #protected还能用来修饰 constructor 构造函数，加了protected修饰符之后，这个类就不能再用来创建实例，只能被子类继承，这个需求我们在讲 ES6 的类的时候讲过，需要用new.target来自行判断，而 TS 则只需用 protected 修饰符即可
class conParent {
  protected constructor() { }
}
// const p3 = new conParent(); //类“conParent”的构造函数是受保护的，仅可在类声明中访问。
class conChild extends conParent {
  constructor() {
    super()
  }
}
const c = new conChild();

// !readonly修饰符
// 将属性设置为只读
class UserInfo {
  readonly name: string;
  constructor(name: string) {
    this.name = name;
  }
}
const user = new UserInfo('fish');
// user.name = 'dd' //无法为“name”赋值，因为它是只读属性。

// !参数属性
// 之前都是在类的定义的顶部初始化实例属性，在 constructor 里接收参数然后对实力属性进行赋值，
// 可以使用参数属性来简化这个过程。
// 参数属性简单来说就是在 constructor 构造函数的参数前面加上访问限定符
class A {
  constructor(name: string) { }
}
const a = new A('aaa');
// console.log(a.name); //类型“A”上不存在属性“name”。
class B {
  constructor(public name: string) { }
}
const b = new B('bbb');
console.log(b.name); //'bbb'

// !静态属性
// 和 ES6 的类一样，在 TS 中一样使用static关键字来指定属性或方法是静态的，实例将不会添加这个静态属性，也不会继承这个静态方法
class staticParent {
  public static age: number = 18;
  public static getAge() {
    return staticParent.age;
  }
  constructor() { }
}
const per = new staticParent();
// console.log(per.age); //属性“age”在类型“staticParent”上不存在。你的意思是改为访问静态成员“staticParent.age”吗?
console.log(staticParent.age); //18

// !可选类属性
// 使用?符号
class Info {
  name: string;
  age?: number;
  constructor(name: string, age?: number, public sex?: string) {
    this.name = name;
    this.age = age;
  }
}
const info3 = new Info('fish');
console.log(info3); //Info {sex: undefined, name: 'fish', age: undefined}
const info4 = new Info('fish', 18);
console.log(info4); //Info {sex: undefined, name: 'fish', age: 18}
const info5 = new Info('fish', 18, 'male');
console.log(info5); //Info {sex: 'male', name: 'fish', age: 18}

// !存储器
// 存值函数和取值函数，也就是在设置属性值的时候调用的函数，和在访问属性值的时候调用的函数
class UserInfo2 {
  private fullName: string;
  constructor(fullName: string) { this.fullName = fullName };
  get getFullName() {
    return this.fullName
  }
  set setFullName(newName: string) {
    console.log(`${newName}`); //seven
    this.fullName = newName
  }
}
const user2 = new UserInfo2('fish');
console.log(user2); //UserInfo2 {fullName: 'fish'}
user2.setFullName = 'seven';
console.log(user2.getFullName); //seven

// !抽象类
// 抽象类一般用来被其他类继承，而不直接用它创建实例。
// 抽象类和类内部定义抽象方法，使用abstract关键字
abstract class Person {
  constructor(public name: string) {
    this.name = name;
  }
  abstract printName(): void
}
class Man extends Person { //非抽象类“Man”不会实现继承自“Person”类的抽象成员“printName”。
  constructor(name: string) {
    super(name)
    this.name = name;
  }
  printName(): void {
    console.log(this.name);
  }
}
const m = new Man('fish');
m.printName() //fish
// const person = new Person('fish'); //无法创建抽象类的实例。

// #abstract关键字不仅可以标记类和类里面的方法，还可以标记类中定义的属性和存取器
abstract class PPerson {
  abstract name: string;
  abstract get insideName(): string;
  abstract set insideName(value: string)
}
// class Pp extends PPerson {
//   name: string; //属性“name”没有初始化表达式，且未在构造函数中明确赋值。
//   insideName: string //属性“insideName”没有初始化表达式，且未在构造函数中明确赋值。
// }

// !实例类型
class Peperson {
  constructor(public name: string) { }
}
let p4: Peperson = new Peperson('fish')
console.log(p4); //Peperson {name: 'fish'}


// !类类型接口
// 使用接口可以强制一个类的定义必须包含某些内容
interface FoodInterface {
  type: string;
}
class FoodClass implements FoodInterface {
  // static type: string;
  constructor(public type: string) { }
}

// !接口继承
// 接口可以继承一个类，当接口继承了该类后，会继承类的成员，但是不包括其实现，也就是只继承成员以及成员类型。接口还会继承类的private和protected修饰的成员，当接口继承的这个类中包含这两个修饰符修饰的成员时，这个接口只可被这个类或他的子类实现
class A1 {
  protected name: string;
  constructor(name: string) { this.name = name }
}
interface I extends A1 { }
// class B1 implements I { } //类“B1”错误实现接口“I”。 //类型 "B1" 中缺少属性 "name"，但类型 "A1" 中需要该属性。
// class C1 implements I { //类“C1”错误实现接口“I”。 //属性“name”受保护，但类型“C1”并不是从“A1”派生的类。
//   name: string;
//   constructor(name: string) { this.name = name }
// }
class D1 extends A1 implements I {
  getName() {
    return this.name
  }
}

// !在泛型中使用类类型
const create = <T>(c: { new(): T }): T => {
  return new c()
}
class InfoS {
  age: number;
  constructor(age: number) { this.age = age }
}
// create(InfoS).age; //类型“typeof InfoS”的参数不能赋给类型“new () => InfoS”的参数。//构造签名的类型不兼容。//不能将类型“new (age: number) => InfoS”分配给类型“new () => InfoS”。
// create(InfoS).name; //类型“InfoS”上不存在属性“name”。